package com.tk.itext7demo.utils.excel;

import com.itextpdf.io.font.PdfEncodings;
import com.itextpdf.io.image.ImageData;
import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.kernel.colors.DeviceGray;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.layout.borders.Border;
import com.itextpdf.layout.borders.SolidBorder;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.properties.TextAlignment;
import com.itextpdf.layout.properties.VerticalAlignment;
import com.itextpdf.layout.renderer.CellRenderer;
import com.itextpdf.layout.renderer.DrawContext;
import com.itextpdf.layout.renderer.IRenderer;
import com.itextpdf.layout.renderer.TableRenderer;
import lombok.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFPicture;

import java.io.IOException;
import java.util.*;


/**
 * @program: easy
 * @description:
 * @author: tjw
 * @create: 2022-11-10 16:24
 **/
public class PdfTableExcel {

    protected ExcelObject excelObject;
    protected Excel excel;
    protected boolean setting = false;
    protected float height;
    protected float heightInPoints;
    protected float width;

    protected PdfFont pdfFont;

    protected PageSize pageSize;

    private final com.itextpdf.layout.element.Cell blankCell = new com.itextpdf.layout.element.Cell().setBorder(Border.NO_BORDER);

    /**
     * <p>Description: Constructor</p>
     * @param excelObject
     */
    public PdfTableExcel(ExcelObject excelObject, PageSize pageSize){
        this.excelObject = excelObject;
        this.excel = excelObject.getExcel();
        this.pageSize = pageSize;
        try {
            this.pdfFont = PdfFontFactory.createFont("/static/SIMSUN.TTC,0", PdfEncodings.IDENTITY_H);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * <p>Description: 获取转换过的Excel内容Table</p>
     * @return PdfPTable
     * @throws IOException
     */
    public com.itextpdf.layout.element.Table getTable() throws IOException {
        Sheet sheet = this.excel.getSheet();
        Workbook wb = this.excel.wb;
        FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator();
        com.itextpdf.layout.element.Table table = toParseContentV2(sheet, evaluator);
        List<MyPictureData> allPictures = getAllPictures(wb);
        table.setNextRenderer(new OverlappingImageTableRenderer(table, allPictures));
        //for (MyPictureData allPicture : allPictures) {
        //    table.setNextRenderer(new OverlappingImageTableRenderer(table, allPictures));
        //}
        return table;
    }


    @Getter
    @Setter
    @NoArgsConstructor
    class PdfPCellInfo2{
        private com.itextpdf.layout.element.Cell pdfPCell;
        private int size;
        private Cell cell;

        public PdfPCellInfo2(com.itextpdf.layout.element.Cell pdfPCell, int size, Cell cell) {
            this.pdfPCell = pdfPCell;
            this.size = size;
            this.cell = cell;
        }
    }

    protected com.itextpdf.layout.element.Table toParseContentV2(Sheet sheet, FormulaEvaluator evaluator) throws IOException {
        int rows = sheet.getPhysicalNumberOfRows();
        for (int i = 0; i < rows; i++) {
            Row row = sheet.getRow(i);
            if (row == null) continue;
            int columns = row.getLastCellNum();
            if (columns < 0) continue;
            heightInPoints += row.getHeightInPoints();
        }
        PdfPCellInfo2[][] tableCell = new PdfPCellInfo2[rows][];
        int maxCol = 0;
        //给二维数字添加值
        for (int i = 0; i < rows; i++) {
            Row row = sheet.getRow(i);
            if (row == null) continue;
            int columns = row.getLastCellNum();
            if (columns < 0) continue;
            if (row.getZeroHeight()) continue;
            PdfPCellInfo2[] cells = new PdfPCellInfo2[columns];
            tableCell[i] = cells;
            maxCol = Math.max(maxCol, columns);
            for (int j = 0; j < columns; j++) {
                Cell cell = row.getCell(j);
                if (cell == null) continue;
                CellRangeAddress range = getColspanRowspanByExcel(row.getRowNum(), cell.getColumnIndex());
                int rowspan = 1;
                int colspan = 1;
                if (range != null) {
                    rowspan = range.getLastRow() - range.getFirstRow() + 1;
                    colspan = range.getLastColumn() - range.getFirstColumn() + 1;
                }
                com.itextpdf.layout.element.Cell pdfpCell = new com.itextpdf.layout.element.Cell(rowspan, colspan);
                pdfpCell.setKeepTogether(true);
                pdfpCell.setBackgroundColor(new DeviceGray(1));
                pdfpCell.setVerticalAlignment(getVAlignByExcel(cell.getCellStyle().getVerticalAlignment().getCode()));
                pdfpCell.setTextAlignment(getTextAlignment(cell.getCellStyle().getAlignment().getCode()));
                getFontByExcel(pdfpCell, cell, evaluator);
                if (sheet.getDefaultRowHeightInPoints() != row.getHeightInPoints()) {
                    pdfpCell.setMinHeight(row.getHeightInPoints()*((pageSize.getHeight()-20*3f)/heightInPoints));
                }
                cells[j] = new PdfPCellInfo2(pdfpCell, colspan, cell);
                j += colspan - 1;
                addBorderByExcel(pdfpCell, cell.getCellStyle(), colspan == 1 ? null : (row.getCell(j) == null ? row.createCell(j) : row.getCell(j)));
            }
        }
        //排除前后不需要的列
        int openCol = 0;
        int endCol = 0;
        boolean open = true;
        boolean end = true;
        for (int i = 0; i < maxCol; i++) {
            if (!open & !end) {
                break;
            }
            endCol = maxCol - 1 - i;
            for (int j = 0; j < rows; j++) {
                if (tableCell[j] == null) continue;
                open = open & (tableCell[j][i] == null || cellIsEmpty(tableCell[j][i].getCell(), evaluator, true));
                end = end & (tableCell[j].length <= endCol || tableCell[j][endCol] == null || cellIsEmpty(tableCell[j][endCol].getCell(), evaluator, false));
            }
            if (open) openCol = i;
            if (i >= endCol) {
                break;
            }
        }
        //计算长度
        List<Float> widths = new ArrayList<>();
        float mw = 0;
        for (int i = 0; i < rows; i++) {
            PdfPCellInfo2[] pdfPCellInfo2s = tableCell[i];
            if (pdfPCellInfo2s == null) continue;
            List<Float> cws = new ArrayList<>();
            float rw = 0;
            for (int j = 0; j < pdfPCellInfo2s.length; j++) {
                if (j > endCol) {
                    break;
                }
                PdfPCellInfo2 pdfPCellInfo2 = pdfPCellInfo2s[j];
                if (pdfPCellInfo2 != null && pdfPCellInfo2.getCell() != null) {
                    float width = getPOIColumnWidth(pdfPCellInfo2.getCell());
                    cws.add(width);
                    rw += width;
                } else {
                    float width = getPOIColumnWidth(sheet.getRow(i).createCell(j));
                    cws.add(width);
                    rw += width;
                }
            }
            if (rw > mw ||  mw == 0) {
                widths = cws;
                mw = rw;
            }
        }
        float[] widthArr = new float[widths.size()];
        for (int i = 0; i < widths.size(); i++) {
            widthArr[i] = widths.get(i);
            width += widths.get(i);
        }
        com.itextpdf.layout.element.Table table = new com.itextpdf.layout.element.Table(widthArr);
        //放入table
        Set<String> set = new HashSet<>();
        for (int i = 0; i < rows; i++) {
            for (int j = openCol; j <= endCol ; j++) {
                if (tableCell[i] == null) continue;
                if (set.contains(i + "" + j)) continue;
                if (tableCell[i].length <= j || tableCell[i][j] == null || tableCell[i][j].getPdfPCell() == null) {
                    Cell cell = sheet.getRow(i).createCell(j);
                    blankCell.setBackgroundColor(new DeviceGray(1));
                    table.addCell(blankCell);
                } else {
                    if (tableCell[i][j].getPdfPCell().getRowspan() > 1) {
                        int colspan = tableCell[i][j].getPdfPCell().getColspan();
                        for (int k = 1; k < tableCell[i][j].getPdfPCell().getRowspan(); k++) {
                            for (int s = 0; s < colspan; s++) {
                                set.add(i + k + "" + (j + s));
                            }
                        }
                    }
                    table.addCell(tableCell[i][j].getPdfPCell());
                    j += tableCell[i][j].getSize() - 1;
                }
            }
        }
        return table;
    }

    public static boolean cellIsEmpty(Cell cell, FormulaEvaluator evaluator, boolean isOpen){
        if (cell == null) {
            return true;
        }
        CellValue evaluate = null;
        String value = "";
        try {
            evaluate = evaluator.evaluate(cell);
        } catch (Exception e){}
        if (evaluate == null || evaluate.getCellType() == null) {
            value = cell.getStringCellValue();
        } else if (evaluate.getCellType() == CellType.STRING) {
            value = evaluate.getStringValue();
        } else if (evaluate.getCellType() == CellType.NUMERIC) {
            value = evaluate.getNumberValue() + "";
        }
        if (StringUtils.isNotBlank(value)) {
            return false;
        } else {
            short left = cell.getCellStyle().getBorderLeft().getCode();//左
            short right = cell.getCellStyle().getBorderRight().getCode();//右
            short top = cell.getCellStyle().getBorderTop().getCode();
            short botton = cell.getCellStyle().getBorderBottom().getCode();
            if (isOpen) {
                return top == 0 & botton == 0 & left == 0;
            } else {
                return top == 0 & botton == 0 & right == 0;
            }
        }
    }

    protected void getFontByExcel(com.itextpdf.layout.element.Cell pdfCell, Cell cell, FormulaEvaluator evaluator) throws IOException {
        CellValue evaluate = null;
        String value = "";
        try {
            evaluate = evaluator.evaluate(cell);
        } catch (Exception e) {
        }

        if (evaluate == null || evaluate.getCellType() == null) {
            value = cell.getStringCellValue();
        } else if (evaluate.getCellType() == CellType.STRING) {
            value = evaluate.getStringValue();
        } else if (evaluate.getCellType() == CellType.NUMERIC) {
            value = evaluate.getNumberValue() + "";
        }
        value = value.replaceAll("  ", " ");
        Workbook wb = excel.getWorkbook();
        CellStyle style = cell.getCellStyle();
        int index = style.getFontIndex();
        Font font = wb.getFontAt(index);
        pdfCell.add(new Paragraph(value).setFixedLeading(0).setMultipliedLeading(1).setFontSize(font.getFontHeightInPoints()*(pageSize.getHeight()-20*3f)/heightInPoints));
        pdfCell.setFont(pdfFont);
        pdfCell.setPadding(0f);
        pdfCell.setPaddingLeft(2f);
        if(font.getBold()){
            pdfCell.setBold();
        }
        HSSFColor color = HSSFColor.getIndexHash().get(font.getColor());
        if(color != null){
            pdfCell.setFontColor(new DeviceGray(POIUtil.getRGB(color)));
        }
        pdfCell.setBackgroundColor(new DeviceGray(1));
    }



    protected void addImageByPOICell(com.itextpdf.layout.element.Cell pdfpCell , Cell cell) throws IOException{
        //POIImage poiImage = new POIImage().getCellImage(cell);
        //byte[] bytes = poiImage.getBytes();
        //if(bytes != null){
        //    pdfpCell.setVerticalAlignment(Element.ALIGN_MIDDLE);
        //    pdfpCell.setHorizontalAlignment(Element.ALIGN_CENTER);
        //    Image image = Image.getInstance(bytes);
        //    pdfpCell.setImage(image);
        //}
    }

    protected float getPixelHeight(float poiHeight){
        return poiHeight / 28.6f * 26f;
    }


    /**
     * <p>Description: 此处获取Excel的列宽像素(无法精确实现,期待有能力的朋友进行改善此处)</p>
     * @param cell
     * @return 像素宽
     */
    protected float getPOIColumnWidth(Cell cell) {
        return excel.getSheet().getColumnWidthInPixels(cell.getColumnIndex());
    }

    protected CellRangeAddress getColspanRowspanByExcel(int rowIndex, int colIndex) {
        CellRangeAddress result = null;
        Sheet sheet = excel.getSheet();
        int num = sheet.getNumMergedRegions();
        for (int i = 0; i < num; i++) {
            CellRangeAddress range = sheet.getMergedRegion(i);
            if (range.getFirstColumn() == colIndex && range.getFirstRow() == rowIndex) {
                result = range;
            }
        }
        return result;
    }

    protected boolean isUsed(int colIndex , int rowIndex){
        boolean result = false;
        Sheet sheet = excel.getSheet();
        int num = sheet.getNumMergedRegions();
        for (int i = 0; i < num; i++) {
            CellRangeAddress range = sheet.getMergedRegion(i);
            int firstRow = range.getFirstRow();
            int lastRow = range.getLastRow();
            int firstColumn = range.getFirstColumn();
            int lastColumn = range.getLastColumn();
            if (firstRow < rowIndex && lastRow >= rowIndex) {
                if(firstColumn <= colIndex && lastColumn >= colIndex){
                    result = true;
                }
            }
        }
        return result;
    }

    protected void addBorderByExcel(com.itextpdf.layout.element.Cell pdfPCellcell, CellStyle style, Cell cell) {
        //左
        if (style.getBorderLeft().getCode() != 0) {
            pdfPCellcell.setBorderLeft(new SolidBorder(0));
        } else {
            pdfPCellcell.setBorderLeft(Border.NO_BORDER);
        }
        //右
        if (cell == null) {
            if (style.getBorderRight().getCode() != 0) {
                pdfPCellcell.setBorderRight(new SolidBorder(0));
            } else {
                pdfPCellcell.setBorderRight(Border.NO_BORDER);
            }
        } else {
            if (cell.getCellStyle().getBorderRight().getCode() != 0) {
                pdfPCellcell.setBorderRight(new SolidBorder(0));
            } else {
                pdfPCellcell.setBorderRight(Border.NO_BORDER);
            }
        }
        //上
        if (style.getBorderTop().getCode() != 0) {
            pdfPCellcell.setBorderTop(new SolidBorder(-1));
        } else {
            pdfPCellcell.setBorderTop(Border.NO_BORDER);
        }
        //下
        if (style.getBorderBottom().getCode() != 0) {
            pdfPCellcell.setBorderBottom(new SolidBorder(-1));
        } else {
            pdfPCellcell.setBorderBottom(Border.NO_BORDER);
        }
    }

    protected VerticalAlignment getVAlignByExcel(short align) {
        VerticalAlignment verticalAlignment = VerticalAlignment.MIDDLE;
        if (align == org.apache.poi.ss.usermodel.VerticalAlignment.BOTTOM.getCode()) {
            verticalAlignment = VerticalAlignment.BOTTOM;
        }
        if (align == org.apache.poi.ss.usermodel.VerticalAlignment.CENTER.getCode()) {
            verticalAlignment = VerticalAlignment.MIDDLE;
        }
        if (align == org.apache.poi.ss.usermodel.VerticalAlignment.JUSTIFY.getCode()) {
            verticalAlignment = VerticalAlignment.MIDDLE;
        }
        if (align == org.apache.poi.ss.usermodel.VerticalAlignment.TOP.getCode()) {
            verticalAlignment = VerticalAlignment.TOP;
        }
        return verticalAlignment;
    }

    protected com.itextpdf.layout.properties.HorizontalAlignment getHAlignByExcel(short align) {
        com.itextpdf.layout.properties.HorizontalAlignment alignment = com.itextpdf.layout.properties.HorizontalAlignment.CENTER;
        if (align == HorizontalAlignment.LEFT.getCode()) {
            alignment = com.itextpdf.layout.properties.HorizontalAlignment.LEFT;
        }
        if (align == HorizontalAlignment.RIGHT.getCode()) {
            alignment = com.itextpdf.layout.properties.HorizontalAlignment.RIGHT;
        }
        if (align == HorizontalAlignment.JUSTIFY.getCode()) {
            alignment = com.itextpdf.layout.properties.HorizontalAlignment.CENTER;
        }
        if (align == HorizontalAlignment.CENTER.getCode()) {
            alignment = com.itextpdf.layout.properties.HorizontalAlignment.CENTER;
        }
        return alignment;
    }


    protected TextAlignment getTextAlignment(short align) {
        TextAlignment alignment = TextAlignment.CENTER;
        if (align == HorizontalAlignment.LEFT.getCode()) {
            alignment = TextAlignment.LEFT;
        }
        if (align == HorizontalAlignment.RIGHT.getCode()) {
            alignment = TextAlignment.RIGHT;
        }
        if (align == HorizontalAlignment.JUSTIFY.getCode()) {
            alignment = TextAlignment.CENTER;
        }
        if (align == HorizontalAlignment.CENTER.getCode()) {
            alignment = TextAlignment.CENTER;
        }
        return alignment;
    }




    public static List<MyPictureData> getAllPictures(Workbook workbook) {
        List<MyPictureData> list = new ArrayList<>();

        List<? extends PictureData> pictureList = workbook.getAllPictures();
        List<ClientAnchorInfo> clientAnchorRecords = getClientAnchorRecords(workbook);

        if (pictureList.size() != clientAnchorRecords.size()) {
            throw new RuntimeException("解析文件中的图片信息出错，找到的图片数量和图片位置信息数量不匹配");
        }

        for (int i = 0; i < pictureList.size(); i++) {
            PictureData pictureData = pictureList.get(i);
            ClientAnchorInfo anchor = clientAnchorRecords.get(i);
            Sheet sheet = anchor.sheet;
            list.add(new MyPictureData(workbook, sheet, pictureData, anchor.anchor));
        }

        return list ;
    }

    private static class ClientAnchorInfo {
        public Sheet sheet;
        public XSSFClientAnchor anchor;

        public XSSFPicture picture;

        public ClientAnchorInfo(Sheet sheet, XSSFClientAnchor anchor, XSSFPicture picture) {
            super();
            this.sheet = sheet;
            this.anchor = anchor;
            this.picture = picture;
        }
    }
    private static List<ClientAnchorInfo> getClientAnchorRecords(Workbook workbook) {
        List<ClientAnchorInfo> list = new ArrayList<>();

        Sheet sheet = null;
        int numSheets = workbook.getNumberOfSheets();
        for(int i = 0; i < numSheets; i++) {
            sheet = workbook.getSheetAt(i);
            Drawing<?> drawingAggregate = sheet.getDrawingPatriarch();
            if(drawingAggregate != null) {
                for (Shape shape : drawingAggregate) {
                    XSSFPicture next = (XSSFPicture) shape;
                    XSSFClientAnchor anchor = (XSSFClientAnchor) next.getAnchor();
                    list.add(new ClientAnchorInfo(sheet, anchor, next));
                }
            }
        }
        return list;
    }


    private class OverlappingImageTableRenderer extends TableRenderer {
        //private ImageData image;

        //private MyPictureData pictureData;

        private List<MyPictureData> pictureDataList;

        //ImageDataFactory.create(allPicture.getData())
        //public OverlappingImageTableRenderer(com.itextpdf.layout.element.Table modelElement, MyPictureData pictureData) {
        //    super(modelElement);
        //    this.pictureData = pictureData;
        //    this.image = ImageDataFactory.create(pictureData.getData());
        //}

        public OverlappingImageTableRenderer(com.itextpdf.layout.element.Table modelElement, List<MyPictureData> pictureDataList) {
            super(modelElement);
            this.pictureDataList = pictureDataList;
        }

        @Override
        public void drawChildren(DrawContext drawContext) {
            super.drawChildren(drawContext);
            for (MyPictureData pictureData : pictureDataList) {
                //高
                float row1Value = pictureData.getRow1Value();
                //宽
                float col1Vale = pictureData.getCol1Vale();
                ImageData image = ImageDataFactory.create(pictureData.getData());
                Iterator<CellRenderer[]> iterator = rows.iterator();
                float height1 = 0;
                while (iterator.hasNext()) {
                    float width1 = 0;
                    CellRenderer[] next = iterator.next();
                    for (CellRenderer cellRenderer : next) {
                        if (cellRenderer == null) continue;
                        width1 += cellRenderer.getBorderAreaBBox().getWidth();
                    }
                    CellRenderer cellRenderer = next[0];
                    if (cellRenderer != null) {
                        height1 += cellRenderer.getBorderAreaBBox().getHeight();
                    }
                }

                //判断是第几页

                if (row1Value > height && row1Value < (height + super.getBorderAreaBBox().getHeight())) {
                    CellRenderer cellRenderer = rows.get(0)[0];
                    //宽
                    float x = cellRenderer.getBorderAreaBBox().getWidth()/ pictureData.getFirstWidth(((com.itextpdf.layout.element.Cell) cellRenderer.getModelElement()).getColspan());
                    //高
                    float y = (pageSize.getHeight()-20*3f)/heightInPoints;//cellRenderer.getBorderAreaBBox().getHeight()/ pictureData.getFirstHigth();
                    //修改getCol0  修改cetRow0
                    drawContext.getCanvas().addImageWithTransformationMatrix(image, pictureData.getImgWidth() * x, 0, 0, pictureData.getImgHight() * y,
                            col1Vale * x + pictureData.getDx1Value() * x + (pageSize.getWidth() - super.getBorderAreaBBox().getWidth()) / 2,
                            pageSize.getHeight() - 20 - (row1Value * y + pictureData.getDy1Value() * y + pictureData.getImgHight() * y - height));
                }
            }
            height += super.getBorderAreaBBox().getHeight();
        }

        @Override
        public IRenderer getNextRenderer() {
            return new OverlappingImageTableRenderer((com.itextpdf.layout.element.Table) modelElement, pictureDataList);
        }
    }
}
